<?php
/**
 * @author tanaka
 */

/************************************ 数値 ************************************/
/********* 科学記法を認めません。 **********/

/**
 * パラメータが実数かどうか判定します。
 *
 * @param mixed $var
 * @return bool
 */
function isFloatNumber($var)
{
	return is_numeric($var) && preg_match("/^-?[0-9]+\.?[0-9]*$/", (string)$var);
}

/**
 * パラメータが実数かどうか判定します。
 *
 * @param mixed $var
 * @return bool
 */
function isFloat($var)
{
	return isFloatNumber($var);
}

/**
 * パラメータが整数かどうか判定します。
 *
 * @param mixed $var
 * @return bool
 */
function isSignedIntNumber($var)
{
	return is_numeric($var) && preg_match("/^-?[0-9]+$/", (string)$var);
}

/**
 * パラメータが整数かどうか判定します。
 *
 * @param mixed $var
 * @return bool
 */
function isSignedInt($var)
{
	return isSignedIntNumber($var);
}

/**
 * パラメータが自然数かどうか判定します。 0を含みません。
 *
 * @param mixed $var
 * @return bool
 */
function isNaturalNumber($var)
{
	return is_numeric($var) && preg_match("/^[1-9][0-9]*$/", (string)$var);
}

/**
 * パラメータが自然数かどうか判定します。 0を含みません。
 * isNaturalNumberのエイリアス
 *
 * @param mixed $var
 * @return bool
 */
function isNatural($var)
{
	return isNaturalNumber($var);
}

/**
 * パラメータが0以上の整数かどうか判定します。
 *
 * @param mixed $var
 * @return bool
 */
function isUnsignedIntNumber($var)
{
	return is_numeric($var) && preg_match("/^[0-9]+$/", (string)$var);
}

/**
 * パラメータが0以上の整数かどうか判定します。
 * isUnsignedIntNumberのエイリアス
 *
 * @param mixed $var
 * @return bool
 */
function isUnsignedInt($var)
{
	return isUnsignedIntNumber($var);
}

/************************************ 文字列 ************************************/

/**
 * 空文字でない文字列か判定します。
 *
 * @param string $string
 * @return bool
 */
function isFillString($string)
{
	return is_string($string) && strlen($string) !== 0;
}

/**
 * 文字エンコーディングを変換します
 *
 * @param	mixed	$data	変換対象データ
 * @param	string	$to		変換したいエンコーディング
 * @param	stirng	$from	変換対象のエンコーディング
 * @return	mixed			変換されたのデータ
 */
function getStringConvertEncoding($data, $to = 'UTF-8', $from = 'ASCII,JIS,UTF-8,EUC-JP,SJIS-win')
{
	if (is_array($data)) {
		foreach ($data as $key => & $value) $value = getStringConvertEncoding($value, $to, $from);
		return $data;
	}
	elseif (is_string($data)) {
		return mb_convert_encoding($data, $to, $from);
	}
	elseif (is_scalar($data)) {
		return $data;
	}
	else {
		return $data;
	}
}

/**
 * 文字列中に含まれるURLにaタグを付与します。
 *
 * @param string $stirng
 * @param bool $isEnableGetParam GETパラメータが含まれているか
 * @return
 */
function addUrlATagInText($stirng, $isContainGetParam = false, $isTargetBlank = false)
{
	$pattern = $isContainGetParam ? '/(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)/' : '/(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/:\@=+\$,%#]+)/';
	$replacement = $isTargetBlank ? '<a href="\\1\\2" target="_blank">\\1\\2</a>' : '<a href="\\1\\2">\\1\\2</a>';
	return preg_replace($pattern, $replacement, $string);
}

/**
 * マルチバイト文字を考慮したfolding(折り畳み)処理
 *
 * @param mixed $str foldingを行う文字列or文字列の配列
 *                   文字列に改行が含まれている場合は改行位置でも分割される
 * @param integer $width 一行の幅(バイト数)。4以上でなければならない
 * @param string $encoding $strの文字エンコーディング
 *                         省略した場合は内部文字エンコーディングを使用する
 * @return array 一行ずつに分けた文字列の配列
 *
 * NOTE: いわゆる半角/全角といった見た目ではなく、
 *       バイト数によって処理が行われるので、文字エンコーディングによって
 *       結果が変わる可能性がある。
 *
 *       例えば半角カナはShift-JISでは1バイトだが、EUC-JPでは2バイトなので、
 *       $width=10の場合Shift-JISなら10文字だが、EUC-JPでは5文字になる。
 *
 *       全角/半角といった見た目で処理をするにはmb_strwidth()を利用した
 *       実装が必要となる。
 *
 * TODO: 日本語禁則処理(Japanese Hyphenation)
 *       行頭禁則文字は濁点/半濁点の応用でいけるので
 *       行末禁則文字の処理を加えれば対応できそう
 *
 *       ……と思ったけど、禁則文字が$widthを超える分だけ並んでたら
 *       どうすればいいんだろう
 *       禁則処理をした結果、桁あふれを起こす場合は禁則処理を無視して
 *       強制的に$widthで改行する、とか？
 */
function mb_fold($str, $width, $encoding = null)
{
	assert('$width >= 4');

	if (!isset($str)) {
		return null;
	}

	if (!isset($encoding)) {
		$encoding = mb_internal_encoding();
	}

	// 元々の配列も文字列中の改行もとにかく展開してひとつの配列にする
	$strings = array();
	foreach ((array)$str as $s) {
		// NOTE: 何故かmb_split()だと改行でうまく分割できない
		//       どうせメジャーなエンコーディングなら制御コードは
		//       leading byteにもtrailing byteにもかぶらないので
		//       preg_split()で良しとする ※JISはアウト
		// NOTE: mb_regex_encoding()を適切に設定してやることで
		//       mb_split()でも正常に分割できるようになったが、
		//       何故かmb_regex_encoding()がJISを受け入れてくれない
		$strings = array_merge($strings, preg_split('/\x0d\x0a|\x0d|\x0a/', $s));
	}

	$lines = array();
	foreach ($strings as $string) {
		// 1文字ずつに分解して足していって、
		// バイト数が$widthを超えたら次の行に回す
		$len = mb_strlen($string, $encoding);
		for ($i = 0, $line = ''; $i < $len; $i++) {
			$char = mb_substr($string, $i, 1, $encoding);

			// 濁点や半濁点が続いていた場合のいい加減な禁則処理
			// ものすごく日本語依存...
			// TODO: Unicodeの結合文字の判定とかで汎用的に処理したい
			if ($i + 1 < $len) {
				$next = mb_substr($string, $i + 1, 1, $encoding);
				$uc = mb_convert_encoding($next, 'UCS-2', $encoding);
				if (in_array($uc, array("\x30\x99", "\x30\x9B", "\x30\x9C",
					"\xFF\x9E", "\xFF\x9F"))) {
					$char .= $next;
					$i++;
				}
			}

			if (strlen($line . $char) > $width) {
				$lines[] = $line;
				$line = $char;
			}
			else {
				$line .= $char;
			}

		}
		$lines[] = $line; // 端数or空行
		}

	return $lines;
}

/************************************ 配列 ************************************/

/**
 * 配列の値が一致する最初のキーを返します。
 * 値の一致には型も見ます。見つからなかった場合nullを返します。
 *
 * @param mixed $needle
 * @param array $array
 * @return mixed | null
 */
function getKeyInArrayFromValue($needle, array $array)
{
	foreach ($array as $key => $value) {
		if ($value === $needle) return $key;
	}

	return null;
}

/**
 * 配列から値が一致したものをすべてunsetします。
 * 値の一致には型も見ます。
 *
 * @param mixed $targetValue
 * @param array $array
 * @return array
 */
function unsetArrayMatchValue($targetValue, array $array)
{
	foreach ($array as $key => $value) {
		if ($value === $targetValue) unset($array[$key]);
	}

	return $array;
}

/**
 * 配列にすべての値があるか返します。
 *
 * @param array|mixed $needles
 * @param array $search
 * @return
 */
function isArrayValuesExist($needles, array $search)
{
	if (!is_array($needles)) return in_array($needles, $search);

	foreach ($needles as $value) {
		if (!in_array($value, $search)) return false;
	}

	return true;
}

/**
 * 配列にすべての値があるか返します。
 * isArrayValuesExistのエイリアス
 *
 * @param array|mixed $needles
 * @param array $search
 * @return
 */
function isArrayExist($needles, array $search)
{
	return isArrayValuesExist($needles, $search);
}

/**
 * 配列にすべてのキーがあるか返します。
 *
 * @param array|mixed $needles
 * @param array $search
 * @return bool
 */
function isArrayKeysExist($needles, array $search)
{
	if (!is_array($needles)) return array_key_exists($needles, $search);

	foreach ($needles as $key) {
		if (!array_key_exists($key, $search)) return false;
	}

	return true;
}

/************************************ その他 ************************************/

/**
 * 標準入力よりユーザーが入力した値を1行取得します。
 * set_time_limit(0)を使用してから使ってください。
 *
 * @return string
 */
function getUserInput()
{
	$stdin = fopen('php://stdin', 'r');
	if ($stdin === false) die();

	$line = trim(fgets($stdin, 64));

	fclose($stdin);

	return $line;
}

/**
 * 乱数を返します
 *
 * @return	string
 */
function random()
{
	return md5(uniqid(mt_rand(), true) . getmypid());
}

/**
 * var_dumpの内容を文字列で返します。
 *
 * @return string
 */
function dumpString($obj)
{
	ob_start();
	var_dump($obj);
	$ret = ob_get_contents();
	ob_end_clean();

	return trim($ret);
}
